import java.awt.Event;
import java.util.List;

import net.java.games.input.Mouse;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.PhysicsRayTestResult;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.effect.ParticleEmitter;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.control.Control;
import com.jme3.scene.shape.Sphere;

public class FirstProto extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener {
	
	private BulletAppState bulletAppState;
    //character
    CharacterControl character;
    Node model;
    //temp vectors
    Vector3f walkDirection = new Vector3f();
    //Door control
    //Materials
    Material matRock;
    Material matBullet;
    //animation
    AnimChannel animationChannel;
    AnimChannel shootingChannel;
    AnimControl animationControl;
    float airTime = 0;
    //camera
    boolean left = false, right = false, up = false, down = false, open = false;
    ChaseCamera chaseCam;
    //bullet
    Sphere bullet;	
    SphereCollisionShape bulletCollisionShape;
    //explosion
    ParticleEmitter effect;
    //brick wall
//    Box brick;
    float bLength = 0.8f;
    float bWidth = 0.4f;
    float bHeight = 0.4f;
    FilterPostProcessor fpp;
    int door1 = 0;
    Geometry door1G;
    
    Level level;

    public static void main(String[] args) {
        FirstProto app = new FirstProto();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        bulletAppState = new BulletAppState();
        bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
        stateManager.attach(bulletAppState);
        
        setupKeys();
        createLight();
        createCharacter();
        setupChaseCamera();
        setupAnimationController();
        setupFilter();
        
        level = new Level(assetManager, bulletAppState);
        rootNode.attachChild(level.makeSpaceStation());
    }

    private void setupFilter() {
        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
        fpp.addFilter(bloom);
        viewPort.addProcessor(fpp);
    }

    private PhysicsSpace getPhysicsSpace() {
        return bulletAppState.getPhysicsSpace();
    }

    private void setupKeys() {
        inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
        inputManager.addListener(this, "wireframe");
        inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping("CharSpace", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("DoorOpen", new KeyTrigger(KeyInput.KEY_O));
        inputManager.addMapping("Shoot", new KeyTrigger(KeyInput.KEY_E));
        inputManager.addMapping("PickUp", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
        inputManager.addMapping("PlaceObject", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addListener(this, "CharLeft");
        inputManager.addListener(this, "CharRight");
        inputManager.addListener(this, "CharUp");
        inputManager.addListener(this, "CharDown");
        inputManager.addListener(this, "CharSpace");
        inputManager.addListener(this, "Shoot");
        inputManager.addListener(this, "CharOpen");
        inputManager.addListener(this, "PickUp");
        inputManager.addListener(this, "PlaceObject");
    }
    
    private void createLight() {
        Vector3f direction = new Vector3f(0.5f, -0.7f, -1).normalizeLocal();
        DirectionalLight dl = new DirectionalLight();
        dl.setDirection(direction);
        dl.setColor(new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f));
        rootNode.addLight(dl);
        
        Vector3f direction2 = new Vector3f(-0.5f, -0.7f, 0.5f).normalizeLocal();
        DirectionalLight d2 = new DirectionalLight();
        d2.setDirection(direction2);
        d2.setColor(new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f));
        rootNode.addLight(d2);
    }

    private void createCharacter() {
        CapsuleCollisionShape capsule = new CapsuleCollisionShape(1f, 3f);
        character = new CharacterControl(capsule, 0.01f);
        model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
        model.setLocalScale(0.5f);
        model.addControl(character);
        character.setPhysicsLocation(new Vector3f(0, 0, 10f));
        rootNode.attachChild(model);
        getPhysicsSpace().add(character);
    }

    private void setupChaseCamera() {
        flyCam.setEnabled(false);
        chaseCam = new ChaseCamera(cam, model, inputManager);
        chaseCam.setMinVerticalRotation(0.9f);
        chaseCam.setDefaultVerticalRotation(0.9f);
		chaseCam.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
    }
    
    private void setupAnimationController() {
        animationControl = model.getControl(AnimControl.class);
        animationControl.addListener(this);
        animationChannel = animationControl.createChannel();
        shootingChannel = animationControl.createChannel();
        shootingChannel.addBone(animationControl.getSkeleton().getBone("uparm.right"));
        shootingChannel.addBone(animationControl.getSkeleton().getBone("arm.right"));
        shootingChannel.addBone(animationControl.getSkeleton().getBone("hand.right"));
    }

    @Override
    public void simpleUpdate(float tpf) {
    	Vector3f camDir = cam.getUp().clone().multLocal(0.1f).add(cam.getDirection().clone().multLocal(0.1f));
        Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
        camDir.y = 0;
        camLeft.y = 0;
        walkDirection.set(0, 0, 0);
        if (left) {
            walkDirection.addLocal(camLeft);
        }
        if (right) {
            walkDirection.addLocal(camLeft.negate());
        }
        if (up) {
            walkDirection.addLocal(camDir);
        }
        if (down) {
            walkDirection.addLocal(camDir.negate());
        }
        if (!character.onGround()) {
            airTime = airTime + tpf;
        } else {
            airTime = 0;
        }
        if (walkDirection.length() == 0) {
            if (!"stand".equals(animationChannel.getAnimationName())) {
                animationChannel.setAnim("stand", 1f);
            }
        } else {
            character.setViewDirection(walkDirection);
            if (airTime > .3f) {
                if (!"stand".equals(animationChannel.getAnimationName())) {
                    animationChannel.setAnim("stand");
                }
            } else if (!"Walk".equals(animationChannel.getAnimationName())) {
                animationChannel.setAnim("Walk", 1f);
            }
        }
        character.setWalkDirection(walkDirection);
        
        if(door1G != null){
			if(door1==-1){
				door1G.setLocalTranslation(door1G.getLocalTranslation().add(-tpf*2, 0f, 0f));
			}
			else if (door1==1){
				door1G.setLocalTranslation(door1G.getLocalTranslation().add(tpf*2, 0f, 0f));
			}
			
	        if(door1G.getLocalTranslation().x<-5f&&door1!=0){
	        	open=false;
	        	door1=0;
	        } 
	        else if(door1G.getLocalTranslation().x>4.5f&&door1!=0){
	        	open=true;
	        	door1=0;
	        }
        }
    }

    public void onAction(String binding, boolean value, float tpf) {
			CollisionResults results = new CollisionResults();
			Vector2f click2d = inputManager.getCursorPosition();
			Vector3f click3d = cam.getWorldCoordinates(
			    new Vector2f(click2d.x, click2d.y), 0f).clone();
			Vector3f dir = cam.getWorldCoordinates(
			    new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
			Ray ray = new Ray(click3d, dir);
			rootNode.collideWith(ray, results);
			int hasObject = 0;
    	
        if (binding.equals("CharLeft")) {
            if (value) {
                left = true;
            } else {
                left = false;
            }
        } else if (binding.equals("CharRight")) {
            if (value) {
                right = true;
            } else {
                right = false;
            }
        } else if (binding.equals("CharUp")) {
            if (value) {
                up = true;
            } else {
                up = false;
            }
        } else if (binding.equals("CharDown")) {
            if (value) {
                down = true;
            } else {
                down = false;
            }
        } else if (binding.equals("CharSpace")) {
            character.jump();
        } else if (binding.equals("Shoot") && !value) {	
  			System.out.println("----- Collisions? " + results.size() + "-----");
  			if (results.size() > 0) {
  				// The closest collision point is what was truly hit:
  				CollisionResult closest = results.getClosestCollision();
  				Geometry g = closest.getGeometry();	
  				if(g.getName().equals("Door")&&!open){  	
  					door1G=g;
  					door1 = 1;
  				} 
  				else if(g.getName().equals("Door")&&open){
  					door1G=g;
					door1 = -1;
  				}
  			}
        }else if (binding.equals("PickUp") && !value){
        	if (results.size() > 0) {
  				CollisionResult closest = results.getClosestCollision();
  				Geometry g = closest.getGeometry();	
  				if(g.getName().equals("brick")){
  					g.removeFromParent();
  					hasObject++;
  				}
        		
        	}
        }else if (binding.equals("PlaceObject") && !value){
        	if (results.size() > 0) {
  				CollisionResult closest = results.getClosestCollision();
  				Geometry g = closest.getGeometry();	
  				if(g.getName().equals("Floor") && hasObject>0){
  					hasObject--;
  				}
        		
        	}
        }
    }
    
    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
        if (channel == shootingChannel) {
            channel.setAnim("stand");
        }
    }

    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
    }

	@Override
	public void collision(PhysicsCollisionEvent arg0) {
		// TODO Auto-generated method stub
		
	}
}

